// ----------------------------------------------------------------------------
// chunk_file.cpp 
// ----------------------------------------------------------------------------
#include <fw/base/serialize/chunk_file.h>
#include <stdio.h>
#include <ctype.h>

namespace fw
{
	static const uint32_t	s_PADDING_WORD = 0xdeadbeef; // チャンクデータを書き込む際のパディングデータ.
	//static const int		s_headerSize   = 8;
	
	namespace
	{
		uint32_t GetDataSize(const Chunk* chunk)
		{
			uint32_t size = 0;
			
			const char* data = chunk->GetData();
			if(data)
			{
				size += chunk->GetDataSize();
			}
			else
			{
				const Chunk* child = chunk->GetChild();
				while(child)
				{
					size += GetDataSize(child);
					size += 8; // ヘッダのサイズ.
					size += (-size & 3);
					child = child->GetSibling();
				}
			}
			return size;
		}
		
		void ChunkWrite(FILE* file, const Chunk* chunk)
		{
			uint32_t id = chunk->GetID();
			uint32_t dataSize = GetDataSize(chunk);
			uint32_t pad = s_PADDING_WORD;
			
			fwrite(&id, 1, sizeof(id), file);
			fwrite(&dataSize, 1, sizeof(dataSize), file);
			
			const char* data = chunk->GetData();
			if(data)
			{
				uint32_t dataSize = chunk->GetDataSize();
				fwrite(data, 1, dataSize, file);
				
				uint32_t padSize = static_cast<uint32_t>(-dataSize & 3);
				fwrite(&pad, 1, padSize, file);
			}
			else
			{
				const Chunk* child = chunk->GetChild();
				while(child)
				{
					ChunkWrite(file, child);
					child = child->GetSibling();
				}
			}
		}
		
		Chunk* GrabChunk(const char** dataPtr, const char* dataEnd)
		{
			Chunk* chunk = nullptr;
			
			char firstChar  = **dataPtr;
//			char id0 = (*dataPtr)[0];
//			char id1 = (*dataPtr)[1];
//			char id2 = (*dataPtr)[2];
//			char id3 = (*dataPtr)[3];
			
			uint32_t chunkID   = *(reinterpret_cast<const uint32_t*>(*dataPtr));
			*dataPtr += 4;
			uint32_t chunkSize = *(reinterpret_cast<const uint32_t*>(*dataPtr));
			*dataPtr += 4;
			
//			printf("id[%c][%c][%c][%c] size=0x%04xbytres.\n", id0, id1, id2, id3, chunkSize);
				
			if(isupper(firstChar))
			{
				chunk = new Chunk(chunkID);
				const char* chunkEnd = *dataPtr + chunkSize;
				
				while(*dataPtr < chunkEnd)
				{
					Chunk* child = GrabChunk(dataPtr, chunkEnd);
					chunk->AddChild(child);
					*dataPtr += ( -(intptr_t)*dataPtr) & 3;
				}
			}
			else
			{
				chunk = new Chunk(chunkID, *dataPtr, chunkSize);
				*dataPtr += chunkSize;
			}
			return chunk;
		}
		
	} // unnamed namespace
	
	int ChunkWrite(const char* fileName, const Chunk* chunk)
	{
		FILE* file = fopen(fileName, "wb");
		if(!file)
		{
			return 0;
		}
		ChunkWrite(file, chunk);
		fseek(file, 0, SEEK_END);
		int size = static_cast<int>(ftell(file));
		fclose(file);
		return size;
	}
	
	const Chunk* ChunkRead(const char* fileName, char* buffer, int bufferSize)
	{
		Chunk* chunk = nullptr;
		FILE* file = fopen(fileName, "rb");
		if(file)
		{
			fseek(file, 0, SEEK_END);
			int fileSize = static_cast<int>(ftell(file));
			fseek(file, 0, SEEK_SET);
			if(fileSize <= bufferSize)
			{
				fread(buffer, 1, fileSize, file);
				const char* read = buffer;
				chunk = GrabChunk(&read, buffer+fileSize);
			}
			fclose(file);
		}
		return chunk;
	}
	
} // namespace fw
